﻿namespace Amazon.Checkout.Drivers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Helpers;
    using JetBrains.Annotations;
    using Models;
    using Orchard;
    using Orchard.ContentManagement;
    using Orchard.ContentManagement.Drivers;
    using Orchard.Localization;
    using Orchard.UI.Notify;
    using Services;
    using ViewModels;

    [UsedImplicitly]
    public class CheckoutByAmazonButtonPartDriver : ContentPartDriver<CheckoutByAmazonButtonPart>
    {
        private readonly IOrchardServices orchardServices;
        private readonly ICheckoutByAmazonService amazonService;
        
        public CheckoutByAmazonButtonPartDriver(IOrchardServices orchardServices, ICheckoutByAmazonService amazonService)
        {
            this.orchardServices = orchardServices;
            this.amazonService = amazonService;
            this.T = NullLocalizer.Instance;
        }

        public Localizer T { get; set; }

        protected override string Prefix
        {
            get { return typeof(CheckoutByAmazonButtonPart).Name; }
        }

        protected override DriverResult Display(CheckoutByAmazonButtonPart part, string displayType, dynamic shapeHelper)
        {
            var settings = this.orchardServices.WorkContext.CurrentSite.As<SiteSettingsPart>();

            var singleItemCart = this.CreateCustomSingleItemAmazonCart(part);
              
            return ContentShape(
                "Parts_CheckoutByAmazonButton",
                () => shapeHelper.Parts_CheckoutByAmazonButton(
                    ContentPart: part,
                    SingleItemCart: singleItemCart,
                    AmazonButtonInitialized: settings.AmazonInitializedButton()));
        }

        protected override DriverResult Editor(CheckoutByAmazonButtonPart part, dynamic shapeHelper)
        {
            var promotions = this.amazonService.GetPromotions().ToList();
            promotions.Insert(0, new PromotionRecord { Id = -1, PromotionCode = this.T("-- Select a Promotion --").Text });

            var taxes = this.amazonService.GetTaxTables().ToList();
            taxes.Insert(0, new TaxTableRecord { Id = -1, Name = this.T("-- Select a Tax Table --").Text });

            var shippingMethods = this.amazonService.GetShippings();

            var viewModel = new CheckoutByAmazonButtonEditPartViewModel { Part = part, Categories = AmazonCheckout.AvailableCategoriesForUpselling, Promotions = promotions, TaxTables = taxes, ShippingMethods = shippingMethods };

            var settings = this.orchardServices.WorkContext.CurrentSite.As<SiteSettingsPart>();

            var method = this.orchardServices.WorkContext.HttpContext.Request.RequestType;

            if (method.Equals("GET", StringComparison.InvariantCultureIgnoreCase))
            {
                if (ValueProvided(settings.PromotionId) && ValueProvided(part.PromotionId))
                {
                    this.orchardServices.Notifier.Warning(this.T("A cart-level promotion has already been defined. The promotion selected for this product will be overriden."));
                }

                if (ValueProvided(settings.TaxId) && ValueProvided(part.TaxId))
                {
                    this.orchardServices.Notifier.Warning(this.T("A cart-level tax has already been defined. The tax selected for this product will be overriden."));
                }

                if (string.IsNullOrEmpty(settings.AssociateTag) && (!string.IsNullOrEmpty(part.UpsellingProducts) || !string.IsNullOrEmpty(part.InclusionCategories) || !string.IsNullOrEmpty(part.ExclusionCategories)))
                {
                    this.orchardServices.Notifier.Error(this.T("The Up-selling items won't be shown until you set a valid Asssociate Id on Amazon module global settings."));
                }
            }

            return ContentShape(
                    "Parts_CheckoutByAmazonButton_Edit",
                    () => shapeHelper.EditorTemplate(TemplateName: "CheckoutByAmazonButton", Model: viewModel, Prefix: this.Prefix));
        }

        protected override DriverResult Editor(CheckoutByAmazonButtonPart part, IUpdateModel updater, dynamic shapeHelper)
        {
            var context = this.orchardServices.WorkContext.HttpContext;

            if (part.PromotionId == -1)
            {
                part.PromotionId = null;
            }

            if (part.TaxId == -1)
            {
                part.TaxId = null;
            }

            part.InclusionCategories = string.Join(",", context.Request.Form.AllKeys.Where(i => i.StartsWith("InclusionCategories.")).Select(i => context.Request.Form[i]));
            part.ExclusionCategories = string.Join(",", context.Request.Form.AllKeys.Where(i => i.StartsWith("ExclusionCategories.")).Select(i => context.Request.Form[i]));
            part.ShippingMethods = context.Request.Form["ShippingMethods"];
            updater.TryUpdateModel(part, this.Prefix, null, null);
            return this.Editor(part, shapeHelper);
        }

        private static bool ValueProvided(int? id)
        {
            return id.HasValue && id.Value > -1;
        }

        private AmazonCheckout.AmazonCart CreateCustomSingleItemAmazonCart(CheckoutByAmazonButtonPart part)
        {
            IEnumerable<string> shippingMethodIds = null;

            if (!string.IsNullOrEmpty(part.ShippingMethods))
            {
                shippingMethodIds = part.ShippingMethods.Split(',');
            }

            var singleItemCart = AmazonCheckout.CreateAmazonCart()
                                               .AddItem(
                                                        part.ProductTitle,
                                                        part.ProductDescription,
                                                        part.ProductPrice,
                                                        part.ImageUrl,
                                                        1,
                                                        part.ProductSku,
                                                        shippingMethodIds: shippingMethodIds);

            this.SetupAmazonCart(singleItemCart, part);
            return singleItemCart;
        }

        private void SetupAmazonCart(AmazonCheckout.AmazonCart cart, CheckoutByAmazonButtonPart part) {
            this.AddShippingMethodsToCart(cart, part);
            this.AddTaxtRulesToCart(cart, part);
            this.AddPromotionsToCart(cart, part);
        }

        private void AddPromotionsToCart(AmazonCheckout.AmazonCart singleItemCart, CheckoutByAmazonButtonPart part)
        {
            var settings = this.orchardServices.WorkContext.CurrentSite.As<SiteSettingsPart>();

            // Single Cart item level promotion);
            if (!ValueProvided(settings.PromotionId) && ValueProvided(part.PromotionId))
            {
                var promotion = this.amazonService.GetPromotion(part.PromotionId.Value);

                if (promotion.DiscountRate != null)
                {
                    singleItemCart.Promotions.AddDiscountRate(promotion.PromotionCode, promotion.Description, Convert.ToDecimal(promotion.DiscountRate));
                }
                else
                {
                    singleItemCart.Promotions.AddFixedAmountDiscount(promotion.PromotionCode, promotion.Description, Convert.ToDecimal(promotion.FixedAmountDiscount));
                }

                singleItemCart.Items.First().PromotionId = promotion.PromotionCode;
            }

            // Cart level promotion)
            if (ValueProvided(settings.PromotionId))
            {
                var promotion = this.amazonService.GetPromotion(settings.PromotionId.Value);

                if (promotion.DiscountRate != null)
                {
                    singleItemCart.WithDiscountRate(promotion.PromotionCode, promotion.Description, Convert.ToDecimal(promotion.DiscountRate));
                }
                else
                {
                    singleItemCart.WithFixedAmountDiscount(promotion.PromotionCode, promotion.Description, Convert.ToDecimal(promotion.FixedAmountDiscount));
                }
            }

            if (!string.IsNullOrEmpty(part.UpsellingProducts) && part.UpsellType.Equals("ByProducts"))
            {
                singleItemCart.AddUpsellingItems(part.UpsellingProducts.Split(','));
            }

            if (!string.IsNullOrEmpty(part.InclusionCategories) && part.UpsellType.Equals("ByCategories"))
            {
                singleItemCart.AddInclusionCategories(part.InclusionCategories.Split(','));
            }

            if (!string.IsNullOrEmpty(part.ExclusionCategories) && part.UpsellType.Equals("ByCategories"))
            {
                singleItemCart.AddExclusionCategories(part.ExclusionCategories.Split(','));
            }
        }

        private void AddShippingMethodsToCart(AmazonCheckout.AmazonCart cart, CheckoutByAmazonButtonPart part) {
            // Shipping Methods
            IEnumerable<string> shippingMethodIds = null;

            if (!string.IsNullOrEmpty(part.ShippingMethods))
            {
                shippingMethodIds = part.ShippingMethods.Split(',');
            }

            if (shippingMethodIds != null)
            {
                foreach (var id in shippingMethodIds)
                {
                    var shipping = this.amazonService.GetShipping(Convert.ToInt32(id));
                    var includedRegions = shipping.GetIncludedRegions();

                    if (!includedRegions.Any())
                    {
                        continue;
                    }

                    var newShipping = cart.ShippingMethods.Add(id, shipping.DisplayableShippingLabel, shipping.PoBoxSupported);

                    switch (shipping.ShipmentRateType)
                    {
                        case "item":
                            newShipping.WithItemQuantityBasedShippingRate(shipping.ItemQuantityBasedShippingRate);
                            break;
                        case "weight":
                            newShipping.WithWeightBasedShippingRate(shipping.WeightBasedShippingRate);
                            break;
                        case "shipment":
                            newShipping.WithShipmentBasedShippingRate(shipping.ShipmentBasedShippingRate);
                            break;
                    }

                    foreach (var region in shipping.GetIncludedRegions())
                    {
                        if (region.GetType() == typeof(AmazonCheckout.USStateRegion))
                        {
                            newShipping.IncludeUSStateRegion(region.GetValue());
                        }

                        if (region.GetType() == typeof(AmazonCheckout.USZipRegion))
                        {
                            newShipping.IncludeUSZipRegion(region.GetValue());
                        }

                        if (region.GetType() == typeof(AmazonCheckout.CountryRegion))
                        {
                            newShipping.IncludeCountryRegion(region.GetValue());
                        }

                        if (region.GetType() == typeof(AmazonCheckout.PredefinedRegion))
                        {
                            newShipping.IncludePredefinedRegion((AmazonCheckout.AmazonPredefinedRegion)Enum.Parse(typeof(AmazonCheckout.AmazonPredefinedRegion), region.GetValue()));
                        }

                        if (region.GetType() == typeof(AmazonCheckout.PostalRegion))
                        {
                            newShipping.IncludePostalRegion(region.GetValue());
                        }
                    }

                    foreach (var region in shipping.GetExcludedRegions())
                    {
                        if (region.GetType() == typeof(AmazonCheckout.USStateRegion))
                        {
                            newShipping.ExcludeUSStateRegion(region.GetValue());
                        }

                        if (region.GetType() == typeof(AmazonCheckout.USZipRegion))
                        {
                            newShipping.ExcludeUSZipRegion(region.GetValue());
                        }

                        if (region.GetType() == typeof(AmazonCheckout.CountryRegion))
                        {
                            newShipping.ExcludeCountryRegion(region.GetValue());
                        }

                        if (region.GetType() == typeof(AmazonCheckout.PredefinedRegion))
                        {
                            newShipping.ExcludePredefinedRegion((AmazonCheckout.AmazonPredefinedRegion)Enum.Parse(typeof(AmazonCheckout.AmazonPredefinedRegion), region.GetValue()));
                        }

                        if (region.GetType() == typeof(AmazonCheckout.PostalRegion))
                        {
                            newShipping.ExcludePostalRegion(region.GetValue());
                        }
                    }
                }
            }
        }

        private void AddTaxtRulesToCart(AmazonCheckout.AmazonCart singleItemCart, CheckoutByAmazonButtonPart part) {
            if (ValueProvided(part.TaxId))
            {
                var taxTable = this.amazonService.GetTaxTable(part.TaxId.Value);

                if (taxTable != null)
                {
                    var taxRules = this.amazonService.GetTaxRules(taxTable.Id);

                    if (taxRules.Any())
                    {
                        var newTaxTable = singleItemCart.TaxTables.Add(taxTable.Id.ToString());

                        foreach (var rule in taxRules)
                        {
                            switch (rule.RegionType)
                            {
                                case "US State Region":
                                    newTaxTable.AddRuleForUSState(rule.Region, rule.Rate, rule.IsShippingTaxed);
                                    break;
                                case "US Zip Region":
                                    newTaxTable.AddRuleForUSZipRegion(rule.Region, rule.Rate, rule.IsShippingTaxed);
                                    break;
                                case "Predefined Region":
                                    AmazonCheckout.AmazonPredefinedRegion region;
                                    Enum.TryParse(rule.Region, true, out region);

                                    newTaxTable.AddRuleForPredefinedRegion(region, rule.Rate, rule.IsShippingTaxed);
                                    break;
                                case "World Region":
                                    newTaxTable.AddRuleForCountry(rule.Region, rule.Rate, rule.IsShippingTaxed);
                                    break;
                                case "Postal Region":
                                    newTaxTable.AddRuleForPostalRegion(rule.Region, rule.Rate, rule.IsShippingTaxed);
                                    break;
                            }
                        }

                        singleItemCart.Items.First().TaxTableId = part.TaxId.ToString();
                    }
                }
            }
        }
    }
}